﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using hello.csharp.common.Exceptions;
using hello.csharp.common.Interfaces;
using hello.csharp.domain;
using hello.csharp.domain.Base;
using hello.csharp.helper;
using hello.csharp.helper.Enums;
using NLog;

namespace hello.csharp.storage.Db.Sqlite
{
    public class DbInitialise : IDbInitialise
    {
        private readonly Logger _logger = LogManager.GetCurrentClassLogger();

        private IEnumerable<Func<IDbConnection>> _openConnectionMethods;

        public DbInitialise()
        {
            FillSqlMap();
        }

        public void FillSqlMap()
        {
            var dirName = Path.Combine(GetRootDirName(), "Sqls");

            var files = Directory.GetFiles(dirName);
            foreach (var file in files)
            {
                var fi = new FileInfo(file);
                var array = fi.Name.Split('.');
                var tableName = array[1];
                var db = array[2];
                var dbType = DatabaseType.SqLite;
                if (DatabaseType.PostgreSql.ToString().ToLower().Contains(db))
                    dbType = DatabaseType.PostgreSql;
                if(!SqlSets.ContainsKey(dbType))
                    SqlSets.Add(dbType, new SqlBox());
                using (var sr = fi.OpenText())
                {
                    var text = sr.ReadToEnd();
                    switch (array[0])
                    {
                        case "i":
                            SqlSets[dbType].Insert[tableName] = text;
                            break;
                        case "t":
                            SqlSets[dbType].Table[tableName] = text;
                            break;
                        case "u":
                            SqlSets[dbType].Update[tableName] = text;
                            break;
                    }
                }
            }
        }

        #region Implementation of IDbInitialise

        /// <summary>
        /// 平台预书写的SQL语句集合
        /// </summary>
        public Dictionary<DatabaseType, SqlBox> SqlSets { get; set; } = new Dictionary<DatabaseType, SqlBox>();

        /// <summary>
        /// 初始化数据库，包括库中的表是否存在，如不存在创建
        /// </summary>
        public bool Run()
        {
            if (_openConnectionMethods == null || !_openConnectionMethods.Any())
                throw new DbInitialiseException();
            foreach (var connection in _openConnectionMethods)
            {
                using (var command = connection.Invoke().CreateCommand())
                {
                    foreach (var dbType in SqlSets.Keys)
                    {
                        CheckTable(command, dbType);
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 设置获取数据库连接的方法
        /// </summary>
        /// <param name="openConnectionMethods">数据库连接的方法集合</param>
        public void SetConnections(IEnumerable<Func<IDbConnection>> openConnectionMethods)
        {
            _openConnectionMethods = openConnectionMethods;
        }

        #endregion

        private static string GetRootDirName()
        {
            var location = Assembly.GetEntryAssembly().Location;
            var dir = new DirectoryInfo(location);
            var dirName = Directory.GetDirectoryRoot(location);
            if (dir.Parent != null)
                dirName = dir.Parent.FullName;
            return dirName;
        }

        private void CheckTable(IDbCommand command, DatabaseType dbType)
        {
            switch (dbType)
            {
                case DatabaseType.SqLite:
                {
                    var sqlMap = SqlSets[dbType].Table;
                    foreach (var pair in sqlMap)
                    {
                        var tableName = pair.Key;
                        command.CommandText = $"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='{tableName}s'";
                        var result = command.ExecuteScalar();
                        _logger.Debug($"检查{tableName}表的状态：{result}");
                        if (!int.TryParse(result.ToString(), out var count) || count <= 0)
                        {
                            _logger.Info($"{tableName}表不存在，准备新建...");
                            CreateSqliteTable(command, pair.Value);
                        }
                    }
                    break;
                }
                case DatabaseType.PostgreSql:
                {
                    break;
                }
            }
        }

        private void CreateSqliteTable(IDbCommand command, string sql)
        {
            command.CommandText = sql;
            var result = command.ExecuteNonQuery();
            _logger.Info($"新建表执行结果：{result}");
        }

    }
}
